From 3f4d2233611e5b4c93606d6eac17dc15b70e3b80 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 16 Nov 2012 17:41:46 -0500 Subject: [PATCH] admin prune: New builtin for cleaning up deployments and repo After a while of pull-deploy cycles, you start to accumulate a lot of them. While the deployment read-only part is hardlinked, the -etc space adds up. Additionally, the repository itself just gets large. The new command "ostree admin prune" deletes everything except the "current" and "previous" deployments. --- Makefile-ostree.am | 1 + src/ostree/ot-admin-builtin-prune.c | 179 ++++++++++++++++++++++++++++ src/ostree/ot-admin-builtins.h | 1 + src/ostree/ot-admin-functions.c | 21 ++++ src/ostree/ot-admin-functions.h | 4 + src/ostree/ot-builtin-admin.c | 1 + 6 files changed, 207 insertions(+) create mode 100644 src/ostree/ot-admin-builtin-prune.c diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 519f77a9..2bff6c9f 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -49,6 +49,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-init.c \ src/ostree/ot-admin-builtin-diff.c \ src/ostree/ot-admin-builtin-deploy.c \ + src/ostree/ot-admin-builtin-prune.c \ src/ostree/ot-admin-builtin-pull-deploy.c \ src/ostree/ot-admin-builtin-update-kernel.c \ src/ostree/ot-admin-builtins.h \ diff --git a/src/ostree/ot-admin-builtin-prune.c b/src/ostree/ot-admin-builtin-prune.c new file mode 100644 index 00000000..5551644a --- /dev/null +++ b/src/ostree/ot-admin-builtin-prune.c @@ -0,0 +1,179 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Colin Walters + */ + +#include "config.h" + +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ostree.h" + +#include + +static gboolean opt_no_repo_prune; + +static GOptionEntry options[] = { + { "no-repo-prune", 0, 0, G_OPTION_ARG_NONE, &opt_no_repo_prune, "Only prune deployment checkouts; don't prune repository", NULL }, + { NULL } +}; + +static gboolean +list_deployments (GFile *from_dir, + GPtrArray *inout_deployments, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GError *temp_error = NULL; + ot_lobj GFileEnumerator *dir_enum = NULL; + ot_lobj GFileInfo *file_info = NULL; + + dir_enum = g_file_enumerate_children (from_dir, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, error); + if (!dir_enum) + goto out; + + while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, error)) != NULL) + { + const char *name; + ot_lobj GFile *child = NULL; + ot_lobj GFile *possible_etc = NULL; + ot_lobj GFile *possible_usr = NULL; + + name = g_file_info_get_name (file_info); + + if (g_str_has_suffix (name, "-etc")) + goto next; + if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) + goto next; + + child = g_file_get_child (from_dir, name); + + possible_etc = ot_gfile_get_child_strconcat (from_dir, name, "-etc", NULL); + /* Bit of a hack... */ + possible_usr = g_file_get_child (child, "usr"); + + if (g_file_query_exists (possible_etc, cancellable)) + g_ptr_array_add (inout_deployments, g_file_get_child (from_dir, name)); + else if (g_file_query_exists (possible_usr, cancellable)) + goto next; + else + { + if (!list_deployments (child, inout_deployments, + cancellable, error)) + goto out; + } + + next: + g_clear_object (&file_info); + } + if (temp_error != NULL) + { + g_propagate_error (error, temp_error); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +gboolean +ot_admin_builtin_prune (int argc, char **argv, GFile *ostree_dir, GError **error) +{ + GOptionContext *context; + gboolean ret = FALSE; + guint i; + ot_lobj GFile *repo_path = NULL; + ot_lobj GFile *current_deployment = NULL; + ot_lobj GFile *previous_deployment = NULL; + ot_lptrarray GPtrArray *deployments = NULL; + __attribute__((unused)) GCancellable *cancellable = NULL; + + context = g_option_context_new ("- Delete untagged deployments and repository objects"); + + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + + if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error)) + goto out; + + deployments = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + if (!list_deployments (ostree_dir, deployments, cancellable, error)) + goto out; + + if (!ot_admin_get_current_deployment (ostree_dir, ¤t_deployment, + cancellable, error)); + if (!ot_admin_get_previous_deployment (ostree_dir, &previous_deployment, + cancellable, error)); + + for (i = 0; i < deployments->len; i++) + { + GFile *deployment = deployments->pdata[i]; + ot_lobj GFile *deployment_etc = NULL; + ot_lobj GFile *parent = NULL; + + if ((current_deployment && g_file_equal (deployment, current_deployment)) + || (previous_deployment && g_file_equal (deployment, previous_deployment))) + continue; + + parent = g_file_get_parent (deployment); + deployment_etc = ot_gfile_get_child_strconcat (parent, ot_gfile_get_basename_cached (deployment), + "-etc", NULL); + + g_print ("Deleting deployment %s\n", ot_gfile_get_path_cached (deployment)); + if (!ot_gio_shutil_rm_rf (deployment, cancellable, error)) + goto out; + /* Note - not atomic; we may be leaving the -etc directory around + * if this fails in the middle =/ + */ + if (!ot_gio_shutil_rm_rf (deployment_etc, cancellable, error)) + goto out; + } + + repo_path = g_file_get_child (ostree_dir, "repo"); + + if (!opt_no_repo_prune) + { + ot_lptrarray GPtrArray *prune_argv = NULL; + ot_lfree char *repo_arg = NULL; + + repo_arg = g_strconcat ("--repo=", ot_gfile_get_path_cached (repo_path), NULL); + + prune_argv = g_ptr_array_new (); + ot_ptrarray_add_many (prune_argv, "ostree", repo_arg, "prune", "--refs-only", "--depth=0", NULL); + g_ptr_array_add (prune_argv, NULL); + + if (!ot_spawn_sync_checked (ot_gfile_get_path_cached (ostree_dir), + (char**)prune_argv->pdata, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL, error)) + goto out; + } + + ret = TRUE; + out: + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index ed38285d..743c4c67 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS gboolean ot_admin_builtin_init (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_deploy (int argc, char **argv, GFile *ostree_dir, GError **error); +gboolean ot_admin_builtin_prune (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_update_kernel (int argc, char **argv, GFile *ostree_dir, GError **error); diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index 85cd4782..63c8266c 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -194,3 +194,24 @@ ot_admin_get_current_deployment (GFile *ostree_dir, return query_symlink_target_allow_noent (current_path, out_deployment, cancellable, error); } + +/** + * ot_admin_get_previous_deployment: + * + * Returns in @out_deployment the full file path of the current + * deployment that the /ostree/previous symbolic link points to, or + * %NULL if none. + */ +gboolean +ot_admin_get_previous_deployment (GFile *ostree_dir, + GFile **out_deployment, + GCancellable *cancellable, + GError **error) +{ + ot_lobj GFile *previous_path = NULL; + + previous_path = g_file_get_child (ostree_dir, "previous"); + + return query_symlink_target_allow_noent (previous_path, out_deployment, + cancellable, error); +} diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index f4e1b74e..af114aa6 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -35,6 +35,10 @@ gboolean ot_admin_get_current_deployment (GFile *ostree_dir, GFile **out_deployment, GCancellable *cancellable, GError **error); +gboolean ot_admin_get_previous_deployment (GFile *ostree_dir, + GFile **out_deployment, + GCancellable *cancellable, + GError **error); G_END_DECLS diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index 70538af7..869a1348 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -46,6 +46,7 @@ static OstreeAdminCommand admin_subcommands[] = { { "init", ot_admin_builtin_init }, { "deploy", ot_admin_builtin_deploy }, { "pull-deploy", ot_admin_builtin_pull_deploy }, + { "prune", ot_admin_builtin_prune }, { "update-kernel", ot_admin_builtin_update_kernel }, { "config-diff", ot_admin_builtin_diff }, { NULL, NULL } -- 2.30.2